package de.zainodis.triplog.util;

import android.content.Context;
import android.util.Log;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import de.zainodis.triplog.R;

/**
 * Created by fzarrai on 8/21/2014.
 */
public class DateTimeUtils {

    private static final String TAG = "DateTimeUtils";
    public static final String READABLE_DATE_STRING = "E, MMM d";
    public static final String FULL_DATE_FORMAT = "E, MMM d yyyy";
    public static final String DAY_OF_WEEK = "EEEE";
    /**
     * Format used in the database.
     */
    public static final String DATE_FORMAT = "yyyyMMdd";
    public static final String MONTH_DAY_FORMAT = "MMMM dd";
    public static final String TIMESTAMP = "yyyyMMdd_HHmmss";


    /**
     * The date/time conversion code is going to be moved outside the asynctask later,
     * so for convenience we're breaking it out into its own method now.
     */
    public static String getReadableDateString(long time) {
        // Because the API returns a unix timestamp (measured in seconds),
        // it must be converted to milliseconds in order to be converted to valid date.
        Date date = new Date(time);
        SimpleDateFormat format = new SimpleDateFormat(READABLE_DATE_STRING);
        return format.format(date).toString();
    }

    /**
     * @param item the date/time value.
     * @return the day of the week for the given date/time value.
     */
    public static String getDayOfWeek(long item) {
        Date date = new Date(item * 1000);
        return new SimpleDateFormat(DAY_OF_WEEK).format(new Date());
    }


    /**
     * @param date the date/time value.
     * @return the day of the week for the given date/time value.
     */
    public static String getDayOfWeek(Date date) {
        return new SimpleDateFormat(DAY_OF_WEEK).format(date);
    }

    /**
     * Converts Date class to a string representation, used for easy comparison and database lookup.
     *
     * @param date The input date
     * @return a DB-friendly representation of the date, using the format defined in DATE_FORMAT.
     */
    public static String toDbDateString(Date date) {
        // Because the API returns a unix timestamp (measured in seconds),
        // it must be converted to milliseconds in order to be converted to valid date.
        SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
        return sdf.format(date);
    }

    /**
     * @param date the date in the format as provided by toDbDateString.
     * @return an instance of Date; null if the given date string was null or an error occurred.
     */
    public static Date dbDateStringToDate(String date) {
        if (date != null) {
            SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
            try {
                Date parsedDate = sdf.parse(date);
                return parsedDate;
            } catch (ParseException e) {
                Log.e(TAG, "Failed to convert to Date instance: " + date, e);
            }
        }

        return null;
    }

    /**
     * @param date the date in the format as provided by toDbDateString.
     * @return human-readable date; null if an error occurred or the given date was null.
     */
    public static String fromDbDateString(String date) {

        SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
        try {
            Date parsedDate = sdf.parse(date);
            return getReadableDateString(parsedDate.getTime());
        } catch (ParseException e) {
            Log.e(TAG, "Failed to convert to readable date: " + date, e);
        }
        return null;
    }

    /**
     * Helper method to convert the database representation of the date into something to display
     * to users. As classy and polished a user experience as "20140102" is, we can do better.
     *
     * @param context Context to use for resource localization
     * @param dateStr The db formatted date string, expected to be of the form specified
     *                in DATE_FORMAT
     * @return a user-friendly representation of the date.
     */
    public static String getFriendlyDayString(Context context, String dateStr) {
        // The day string for forecast uses the following logic:
        // For today: "Today, June 8"
        // For tomorrow: "Tomorrow"
        // For the next 5 days: "Wednesday" (just the day name)
        // For all days after that: "Mon Jun 8"
        Date todayDate = new Date();
        Date inputDate = dbDateStringToDate(dateStr);
        // If the date we're building the String for is today's date, the format
        // is "Today, June 24"
        if (toDbDateString(todayDate).equals(dateStr)) {
            String today = context.getString(R.string.today);
            return context.getString(
                    R.string.format_full_friendly_date,
                    today,
                    getFormattedMonthDay(context, dateStr));
        } else {
            Calendar cal = Calendar.getInstance();
            cal.setTime(todayDate);
            cal.add(Calendar.DATE, 7);
            String weekFutureString = toDbDateString(cal.getTime());
            if (dateStr.compareTo(weekFutureString) < 0) {
                // If the input date is less than a week in the future, just return the day name.
                return getDayName(context, dateStr);
            } else {
                // Otherwise, use the form "Mon Jun 3"
                SimpleDateFormat shortenedDateFormat = new SimpleDateFormat("EEE MMM dd");
                return shortenedDateFormat.format(inputDate);
            }
        }
    }

    /**
     * Given a day, returns just the name to use for that day.
     * E.g "today", "tomorrow", "wednesday".
     *
     * @param context Context to use for resource localization
     * @param dateStr The db formatted date string, expected to be of the form specified
     *                in DATE_FORMAT
     * @return
     */
    public static String getDayName(Context context, String dateStr) {
        SimpleDateFormat dbDateFormat = new SimpleDateFormat(DATE_FORMAT);
        try {
            Date inputDate = dbDateFormat.parse(dateStr);
            Date todayDate = new Date();
            // If the date is today, return the localized version of "Today" instead of the actual
            // day name.
            if (toDbDateString(todayDate).equals(dateStr)) {
                return context.getString(R.string.today);
            } else {
                // If the date is set for tomorrow, the format is "Tomorrow".
                Calendar cal = Calendar.getInstance();
                cal.setTime(todayDate);
                cal.add(Calendar.DATE, 1);
                Date tomorrowDate = cal.getTime();
                if (toDbDateString(tomorrowDate).equals(
                        dateStr)) {
                    return context.getString(R.string.tomorrow);
                } else {
                    // Otherwise, the format is just the day of the week (e.g "Wednesday".
                    SimpleDateFormat dayFormat = new SimpleDateFormat(DAY_OF_WEEK);
                    return dayFormat.format(inputDate);
                }
            }
        } catch (ParseException e) {
            Log.e(TAG, "Failed to parse date.", e);
            // It couldn't process the date correctly.
            return "";
        }
    }

    /**
     * Converts db date format to the format "Month day", e.g "June 24".
     *
     * @param context Context to use for resource localization
     * @param dateStr The db formatted date string, expected to be of the form specified
     *                in Utility.DATE_FORMAT
     * @return The day in the form of a string formatted "December 6"
     */
    public static String getFormattedMonthDay(Context context, String dateStr) {
        SimpleDateFormat dbDateFormat = new SimpleDateFormat(DATE_FORMAT);
        try {
            Date inputDate = dbDateFormat.parse(dateStr);
            SimpleDateFormat monthDayFormat = new SimpleDateFormat(MONTH_DAY_FORMAT);
            String monthDayString = monthDayFormat.format(inputDate);
            return monthDayString;
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * @param date a Date instance to format.
     * @return the formatted date using FULL_DATE_FORMAT; null if the given date is null.
     */
    public static String getFormattedDate(Date date) {
        return getFormattedDate(date, FULL_DATE_FORMAT);
    }

    /**
     * @param format the date format to use.
     * @param date   a Date instance to format.
     * @return the formatted date using FULL_DATE_FORMAT; null if the given date is null.
     */
    public static String getFormattedDate(Date date, String format) {
        if (date == null) {
            return null;
        }
        SimpleDateFormat dateFormat = new SimpleDateFormat(format);
        return dateFormat.format(date);
    }
}
